#!/usr/bin/env python
# -*- coding: utf-8 -*-

"""
失败重试机制
提供操作失败后的自动重试功能
"""

import time
import random
import logging
from typing import Callable, Any, Dict, Optional

logger = logging.getLogger(__name__)

class RetryHandler:
    """
    重试处理器，提供失败操作的重试机制
    """
    
    def __init__(self, max_attempts: int = 3, initial_backoff: float = 1.0, 
                 backoff_factor: float = 2.0, jitter: bool = True):
        """
        初始化重试处理器
        
        Args:
            max_attempts: 最大尝试次数
            initial_backoff: 初始等待时间(秒)
            backoff_factor: 退避系数，每次重试等待时间增长因子
            jitter: 是否添加随机抖动
        """
        self.max_attempts = max_attempts
        self.initial_backoff = initial_backoff
        self.backoff_factor = backoff_factor
        self.jitter = jitter
        
        # 重试统计
        self.retry_stats = {
            "total_attempts": 0,
            "successful_retries": 0,
            "failed_operations": 0,
            "retry_by_error": {}
        }
    
    def with_retry(self, func: Callable, *args, **kwargs) -> Any:
        """
        使用重试机制执行函数
        
        Args:
            func: 要执行的函数
            *args: 位置参数
            **kwargs: 关键字参数
            
        Returns:
            Any: 函数执行结果
            
        Raises:
            Exception: 达到最大重试次数后仍失败
        """
        attempt = 0
        last_exception = None
        
        while attempt < self.max_attempts:
            try:
                # 执行函数
                result = func(*args, **kwargs)
                
                # 如果是第一次尝试以外成功，记录成功重试
                if attempt > 0:
                    self.retry_stats["successful_retries"] += 1
                    logger.info(f"操作在第{attempt+1}次尝试后成功")
                
                return result
                
            except Exception as e:
                attempt += 1
                last_exception = e
                self.retry_stats["total_attempts"] += 1
                
                # 记录错误类型统计
                error_type = type(e).__name__
                if error_type not in self.retry_stats["retry_by_error"]:
                    self.retry_stats["retry_by_error"][error_type] = 0
                self.retry_stats["retry_by_error"][error_type] += 1
                
                # 如果已达到最大尝试次数，记录失败并抛出异常
                if attempt >= self.max_attempts:
                    self.retry_stats["failed_operations"] += 1
                    logger.error(f"操作在{self.max_attempts}次尝试后仍然失败: {e}")
                    raise e
                
                # 计算等待时间
                wait_time = self._calculate_backoff(attempt)
                
                logger.warning(f"操作失败，将在{wait_time:.2f}秒后重试 ({attempt}/{self.max_attempts}): {e}")
                
                # 等待后重试
                time.sleep(wait_time)
    
    def handle_failure(self, operation: str, error: str, attempt: int, max_attempts: int) -> float:
        """
        处理单次失败，计算并返回下次重试前应等待的时间
        
        Args:
            operation: 操作名称
            error: 错误信息
            attempt: 当前尝试次数
            max_attempts: 最大尝试次数
            
        Returns:
            float: 下次重试前应等待的时间(秒)
        """
        # 确保尝试次数合理
        if attempt >= max_attempts:
            logger.warning(f"操作 {operation} 已达到最大尝试次数 {max_attempts}")
            return 0
        
        # 更新统计信息
        self.retry_stats["total_attempts"] += 1
        
        # 记录错误类型统计
        error_type = error.split(':')[0] if ':' in error else "UnknownError"
        if error_type not in self.retry_stats["retry_by_error"]:
            self.retry_stats["retry_by_error"][error_type] = 0
        self.retry_stats["retry_by_error"][error_type] += 1
        
        # 计算等待时间
        wait_time = self._calculate_backoff(attempt)
        
        logger.warning(f"操作 {operation} 失败，建议在{wait_time:.2f}秒后重试 ({attempt}/{max_attempts}): {error}")
        
        return wait_time
    
    def _calculate_backoff(self, attempt: int) -> float:
        """
        计算退避等待时间
        
        Args:
            attempt: 当前尝试次数
            
        Returns:
            float: 等待时间(秒)
        """
        # 指数退避
        wait_time = self.initial_backoff * (self.backoff_factor ** (attempt - 1))
        
        # 加入随机抖动
        if self.jitter:
            wait_time *= (0.5 + random.random())
        
        return wait_time
    
    def get_stats(self) -> Dict[str, Any]:
        """
        获取重试统计信息
        
        Returns:
            Dict: 统计信息
        """
        return self.retry_stats
    
    def reset_stats(self):
        """重置统计信息"""
        self.retry_stats = {
            "total_attempts": 0,
            "successful_retries": 0,
            "failed_operations": 0,
            "retry_by_error": {}
        }
        logger.debug("重试统计信息已重置") 